package org.redmaple.core.template.dao.hibernate;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.sql.JoinType;
import org.redmaple.core.pkstrategy.UUIDGeneratedStrategy;
import org.redmaple.core.retrieving.RedmapleCriteria;
import org.redmaple.core.retrieving.RedmapleCriterion;
import org.redmaple.core.retrieving.Sort;
import org.redmaple.core.retrieving.hibernate.CriterionPlugin;
import org.redmaple.core.retrieving.hibernate.OrderPlugin;
import org.redmaple.core.template.dao.IGenericDao;
import org.redmaple.core.template.model.BaseModel;
import org.springframework.util.CollectionUtils;

/**
 * 一般化的DAO基类，实现了CURD功能
 * 
 * @author Xiangyun
 *
 * @param <T>
 */
public abstract class GenericDaoImpl<T extends BaseModel> extends
		RedmapleHibernateTemplate<T> implements IGenericDao<T> {

	/**
	 * 参数化类型的class对象
	 */
	private Class<T> parameterizedTypeClass;
	
	/**
	 * 外键或伪外键的映射，方便使用LEFT JOIN查询
	 */
	private Map<String, Class<?>> foreignKeysMap;

	@SuppressWarnings("unchecked")
	public GenericDaoImpl() {
		// 获取参数化类型的class对象
		this.parameterizedTypeClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
		
		// 获取多对一和一对一的外键约束class对象
		foreignKeysMap = new HashMap<String, Class<?>>();
		Field[] fields = this.parameterizedTypeClass.getDeclaredFields();
		for(Field field : fields) {
			if(field.isAnnotationPresent(ManyToOne.class) ||
					field.isAnnotationPresent(OneToOne.class)) {
				foreignKeysMap.put(field.getName(), field.getClass());
			}
		}
		logger.debug("初始化成功 " + getClass().getName());
	}

	protected String getParameterizedTypeName() {
		return parameterizedTypeClass.getName();
	}

	public boolean exists(String id) {
		return (super.findOne(id) != null);
	}
	
	public List<T> findListByCondition(RedmapleCriteria redmapleCriteria,
			List<Sort> sortValues) {
		DetachedCriteria criteria = getBaseCriteria();
		List<RedmapleCriterion> listCriterions = redmapleCriteria.getListCriterions();
		for(RedmapleCriterion criterion : listCriterions) {
			criteria.add(CriterionPlugin.convert(criterion, getObjectName()));
		}
		if(CollectionUtils.isEmpty(sortValues)){
			for(Sort sort : sortValues) {
				criteria.addOrder(OrderPlugin.convert(sort, getObjectName()));
			}
		}
		return super.findListByCriteria(criteria);
	}
	
	protected DetachedCriteria getBaseCriteria(){
		DetachedCriteria criteria = DetachedCriteria.forClass(
				parameterizedTypeClass, getObjectName());
		Set<String> foreignKeysMapKeySet = foreignKeysMap.keySet();
		for(String fieldName : foreignKeysMapKeySet) {
			criteria.createAlias(fieldName, fieldName, JoinType.LEFT_OUTER_JOIN);
		}
		return criteria;
	}
	
	protected void setGeneratedStrategy() {
		// 默认UUID主键生成策略
		this.generatedStrategy = new UUIDGeneratedStrategy();
	}

	/**
	 * 获取HQL中查询主体的名称,一般由具体类采用返回字符串常量来实现。
	 * 
	 * @return 查询主体的名称
	 */
	public abstract String getObjectName();
}
